﻿#region Using Statements
using System;
using Microsoft.Xna.Framework;
#endregion

namespace StarFox.ScreenComponents
{
    /// <summary>
    /// Implementation of the StarShip's velocity control.
    /// </summary>
    internal class StarShipVelocity
    {
        #region VelocityState

        enum VelocityState
        {
            IDLE,
            BOOST,
            BRAKE
        };

        VelocityState state = VelocityState.IDLE;

        #endregion

        #region Fields

        IShipInput input;
        PlayerIndex playerIndex;

        float velocity = 0.0f;

        #endregion

        #region Boost, Brake

        /// <summary>
        /// Sets the time needed by the StarShip to brake completely.
        /// </summary>
        public TimeSpan BrakeTime
        {
            private get;
            set;
        }

        /// <summary>
        /// Sets the time needed by the StarShip to boost completely.
        /// </summary>
        public TimeSpan BoostTime
        {
            private get;
            set;
        }

        /// <summary>
        /// Sets the time needed by the StarShip to return to idle velocity.
        /// </summary>
        public TimeSpan IdleTime
        {
            private get;
            set;
        }

        #endregion

        #region Start, Stop

        bool stopped;

        /// <summary>
        /// Causes the StarShip to start moving.
        /// </summary>
        public void Start()
        {
            stopped = false;
        }

        /// <summary>
        /// Causes the StarShip to stop moving.
        /// </summary>
        public void Stop()
        {
            stopped = true;
        }

        #endregion

        #region Boost

        /// <summary>
        /// Gets or sets the StarShip's boost level available.
        /// </summary>
        public float Boost
        {
            get;
            set;
        }

        /// <summary>
        /// Sets the duration of the StarShip's boost.
        /// </summary>
        public TimeSpan BoostDuration
        {
            private get;
            set;
        }

        /// <summary>
        /// Called when the StarShip gets an extra boost power-up.
        /// </summary>
        public void IncreaseBoost()
        {
            Boost += 0.20f;
            Boost = MathHelper.Clamp(Boost, 0.0f, 1.0f);
        }

        #endregion

        #region Fuel

        /// <summary>
        /// Gets or sets the StarShip's fuel level available.
        /// </summary>
        public float Fuel
        {
            get;
            set;
        }

        /// <summary>
        /// Sets the duration of the StarShip's fuel when idle.
        /// </summary>
        public TimeSpan FuelDurationIdle
        {
            private get;
            set;
        }

        /// <summary>
        /// Sets the duration of the StarShip's fuel when boosting or breaking.
        /// </summary>
        public TimeSpan FuelDurationBoost
        {
            private get;
            set;
        }

        /// <summary>
        /// Called when the StarShip gets an extra fuel power-up.
        /// </summary>
        public void IncreaseFuel()
        {
            Fuel += 0.50f;
            Fuel = MathHelper.Clamp(Fuel, 0.0f, 1.0f);
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of this class.
        /// </summary>
        /// <param name="input">StarShip input management functions.</param>
        /// <param name="playerIndex">Specifies which player to read input from.</param>
        public StarShipVelocity(IShipInput input, PlayerIndex playerIndex)
        {
            this.input = input;
            this.playerIndex = playerIndex;
        }

        #endregion

        #region Velocity

        /// <summary>
        /// Computes the StarShip's velocity (0.0 -> minimum velocity - 1.0 -> maximum velocity). 
        /// </summary>
        /// <param name="gameTime">Time elapsed since the last call to this method.</param>
        /// <returns>StarShip's velocity.</returns>
        public float ComputeShipVelocity(GameTime gameTime)
        {
            if (stopped)
                return 0.0f;

            bool isBoost = input.IsBoost(playerIndex);
            bool isBrake = input.IsBrake(playerIndex);

            switch (state)
            {
                case VelocityState.IDLE:
                    {
                        #region Actions

                        float timeDelta;

                        // velocity
                        if (velocity < 0.5f)
                        {
                            timeDelta = (float)(gameTime.ElapsedGameTime.TotalMilliseconds / IdleTime.TotalMilliseconds);
                            velocity += timeDelta;
                            velocity = MathHelper.Clamp(velocity, 0.0f, 0.5f);
                        }
                        else if (velocity > 0.5f)
                        {
                            timeDelta = (float)(gameTime.ElapsedGameTime.TotalMilliseconds / IdleTime.TotalMilliseconds);
                            velocity -= timeDelta;
                            velocity = MathHelper.Clamp(velocity, 0.5f, 1.0f);
                        }

                        // fuel level
                        timeDelta = (float)(gameTime.ElapsedGameTime.TotalMilliseconds / FuelDurationIdle.TotalMilliseconds);
                        Fuel -= timeDelta;
                        Fuel = MathHelper.Clamp(Fuel, 0.0f, 1.0f);

                        #endregion

                        #region Transitions

                        if (isBoost && !isBrake && Boost > 0.0f && Fuel > 0.0f) { state = VelocityState.BOOST; }
                        else if (!isBoost && isBrake && Boost > 0.0f && Fuel > 0.0f) { state = VelocityState.BRAKE; }

                        #endregion

                        break;
                    }
                case VelocityState.BOOST:
                    {
                        #region Actions

                        // velocity
                        float timeDelta = (float)(gameTime.ElapsedGameTime.TotalMilliseconds / BoostTime.TotalMilliseconds);
                        velocity += timeDelta;
                        velocity = MathHelper.Clamp(velocity, 0.0f, 1.0f);

                        // boost level
                        timeDelta = (float)(gameTime.ElapsedGameTime.TotalMilliseconds / BoostDuration.TotalMilliseconds);
                        Boost -= timeDelta;
                        Boost = MathHelper.Clamp(Boost, 0.0f, 1.0f);

                        // fuel level
                        timeDelta = (float)(gameTime.ElapsedGameTime.TotalMilliseconds / FuelDurationBoost.TotalMilliseconds);
                        Fuel -= timeDelta;
                        Fuel = MathHelper.Clamp(Fuel, 0.0f, 1.0f);

                        #endregion

                        #region Transtitions

                        if (!isBoost && isBrake && Boost > 0.0f && Fuel > 0.0f) { state = VelocityState.BRAKE; }
                        else if (!isBoost && !isBrake || Boost == 0.0f || Fuel == 0.0f) { state = VelocityState.IDLE; }

                        #endregion

                        break;
                    }
                case VelocityState.BRAKE:
                    {
                        #region Actions

                        // velocity
                        float timeDelta = (float)(gameTime.ElapsedGameTime.TotalMilliseconds / BrakeTime.TotalMilliseconds);
                        velocity -= timeDelta;
                        velocity = MathHelper.Clamp(velocity, 0.0f, 1.0f);

                        // boost level
                        timeDelta = (float)(gameTime.ElapsedGameTime.TotalMilliseconds / BoostDuration.TotalMilliseconds);
                        Boost -= timeDelta;
                        Boost = MathHelper.Clamp(Boost, 0.0f, 1.0f);

                        // fuel level
                        timeDelta = (float)(gameTime.ElapsedGameTime.TotalMilliseconds / FuelDurationBoost.TotalMilliseconds);
                        Fuel -= timeDelta;
                        Fuel = MathHelper.Clamp(Fuel, 0.0f, 1.0f);

                        #endregion

                        #region Transitions

                        if (isBoost && !isBrake && Boost > 0.0f && Fuel > 0.0f) { state = VelocityState.BOOST; }
                        else if (!isBoost && !isBrake || Boost == 0.0f || Fuel == 0.0f) { state = VelocityState.IDLE; }

                        #endregion

                        break;
                    }
            }

            return velocity;
        }

        #endregion
    }
}
